#include <reg51.h>
#include <intrins.h>

// 引脚定义
sbit LCD_RS = P1_0;
sbit LCD_RW = P1_1;
sbit LCD_E  = P1_2;
#define LCD_DATA P0  // 数据口接P0

// 毫秒级延时函数
void Delay_ms(unsigned int ms) {
    unsigned int i, j;
    for(i = 0; i < ms; i++)
        for(j = 0; j < 112; j++);  // 大致1ms延时（11.0592MHz晶振）
}

// 检测LCD忙状态
bit LCD_CheckBusy() {
    bit busy;
    LCD_RS = 0;  // 指令模式
    LCD_RW = 1;  // 读操作
    LCD_E = 1;   // 使能有效
    _nop_();_nop_();_nop_();_nop_();  // 等待数据稳定
    busy = (bit)(LCD_DATA & 0x80);  // 读取D7位（忙标志）
    LCD_E = 0;   // 关闭使能
    return busy;
}

// 等待LCD空闲
void LCD_WaitReady() {
    while(LCD_CheckBusy());  // 循环检测直到不忙
}

// 向LCD写入指令
void LCD_WriteCmd(unsigned char cmd) {
    LCD_WaitReady();
    LCD_RS = 0;  // 指令模式
    LCD_RW = 0;  // 写操作
    LCD_DATA = cmd;  // 送指令
    LCD_E = 1;   // 高脉冲触发
    _nop_();_nop_();  // 保持高电平
    LCD_E = 0;   // 关闭使能
}

// 向LCD写入数据
void LCD_WriteData(unsigned char dat) {
    LCD_WaitReady();
    LCD_RS = 1;  // 数据模式
    LCD_RW = 0;  // 写操作
    LCD_DATA = dat;  // 送数据
    LCD_E = 1;   // 高脉冲触发
    _nop_();_nop_();
    LCD_E = 0;
}

// 初始化LCD
void LCD_Init() {
    Delay_ms(15);  // 上电延时
    LCD_WriteCmd(0x38);  // 8位数据，2行显示，5×8点阵
    LCD_WriteCmd(0x0C);  // 显示开，光标关，闪烁关
    LCD_WriteCmd(0x06);  // 光标右移，屏幕不滚动
    LCD_WriteCmd(0x01);  // 清屏
    Delay_ms(1);
}

// 设置光标位置（x:列0-15，y:行0-1）
void LCD_SetCursor(unsigned char x, unsigned char y) {
    unsigned char addr;
    if(y == 0)
        addr = 0x00 + x;  // 第一行起始地址0x00
    else
        addr = 0x40 + x;  // 第二行起始地址0x40
    LCD_WriteCmd(addr | 0x80);  // 设置DDRAM地址
}

// 显示字符串
void LCD_ShowStr(unsigned char x, unsigned char y, unsigned char *str) {
    LCD_SetCursor(x, y);
    while(*str != '\0') {
        LCD_WriteData(*str);
        str++;
    }
}

void main() {
    LCD_Init();
    LCD_ShowStr(0, 0, "Hello, World!");  // 第一行显示
    LCD_ShowStr(2, 1, "51 LCD1602 Test"); // 第二行显示
    while(1);  // 循环等待
}
